#include <iostream>
#include <fstream>

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>

#include "porridgedb.h"
#include "session.h"

using namespace Porridge;
using namespace boost::posix_time;

namespace
{

double writeTestSession(PorridgeDB * db,
                        Buffer<PorridgeDB> * buffer,
                        size_t sessionCount, //rows count in the main table
                        size_t uinCount,     //uin count per one table
                        size_t uinLength)
{
    ptime startTime = microsec_clock::universal_time();
    {
        //begin new session
        size_t tableID = db->startSession();
        if(!tableID)
            return 0;

        //write main table
        db->tableBegin("session");
        for(int i = 0; i < sessionCount; ++i)
        {
            Session session(i);
            session.write(tableID, buffer);
        }
        buffer->flush();
        db->tableEnd();

        //write table with one-to-many
        db->tableBegin("uin");
        for(int i = 0; i < uinCount; ++i)
        {
            UIN uin(uinLength, sessionCount);
            uin.write(tableID, buffer);
        }
        buffer->flush();
        db->tableEnd();

        db->createIndexes();
    }
    ptime finishTime = microsec_clock::universal_time();

    double seconds = double(time_duration(finishTime - startTime).total_milliseconds()) / 1000;

    std::cout << "insert " << sessionCount
              << " sessions and " << uinCount
              << " uins during " << seconds
              << " seconds, " << double(sessionCount + uinCount) / seconds
              << " rows per second, "
              << (sessionCount * sessionSize() + uinCount * (uinSize() + uinLength)) / (1024 * 1024 * seconds)
              << " raw data Mbytes per second" << std::endl;

    return seconds;
}

} //anonimous namespace

int main(int argc, char ** argv)
{   
    PorridgeDB porridgeDB;

    std::string fileName = "create_indexes.sql";
    if(argc > 1)
        fileName = argv[1];
    std::vector<std::string> queries;
    std::string query;
    std::ifstream queryFile(fileName);
    while (queryFile >> query)
       queries.push_back(query);

    size_t sessionCount = 10;
    if(argc > 2)
        sessionCount = boost::lexical_cast<int64_t>(argv[2]);

    double uinFactor = 1.2;
    if(argc > 3)
        uinFactor = boost::lexical_cast<double>(argv[3]);

    size_t uinLength = 20;
    if(argc > 4)
        uinLength = boost::lexical_cast<size_t>(argv[4]);

    size_t tableCount = std::numeric_limits<size_t>::max();
    if(argc > 5)
        tableCount = boost::lexical_cast<size_t>(argv[5]);

    size_t maxBufferSize = 1024 * 1024 * 100;
    if(argc > 6)
        maxBufferSize = boost::lexical_cast<size_t>(argv[6]);

    size_t maxThreadCount = 0;
    if(argc > 7)
        maxThreadCount = boost::lexical_cast<size_t>(argv[7]);

    //connection options same as one for libpq::PQconnectdb
    //host: hostname
    //hostaddr: numeric IP
    //port, dbname, user, password
    porridgeDB.setConnetctionInfo("dbname = echelon_1 user = echelon");
    porridgeDB.setNewSessionQuery("new_session");
    porridgeDB.setQueryThreadLimit(maxThreadCount);//overwrite selfdefined value
    porridgeDB.setDBSizeLimit(1024 * 1024 * 1024);
    porridgeDB.setProcessingQueries(queries.begin(), queries.end());

    size_t uinCount = sessionCount * uinFactor;
    if(porridgeDB.connect())
    {
        Buffer<PorridgeDB> buffer;
        buffer.output = &porridgeDB;
        buffer.sizeLimit = maxBufferSize;

        double totalSeconds = 0;

        for(int i = 0; i < tableCount; ++i)
        {
            double seconds = writeTestSession(&porridgeDB, &buffer, sessionCount, uinCount, uinLength);
            if(abs(seconds) < std::numeric_limits<double>::epsilon())
            {
                std::cout << "writeTest() function fail!" << std::endl;
                return -1;
            }
            totalSeconds += seconds;
        }
        std::cout << "====================================================================================" << "\n";
        std::cout << "Total: " << "\n";
        std::cout << "insert " << sessionCount * tableCount
                  << " sessions and " << uinCount * tableCount
                  << " uins during " << totalSeconds
                  << " seconds, " << double((sessionCount + uinCount) * tableCount) / totalSeconds
                  << " rows per second" << std::endl;
        std::cout << "====================================================================================" << "\n";
        return 0;
    }
    std::cout << "Porridge DB connect fail!" << std::endl;
    return -1;
}
